/*
 * @Author: xiaosihan 
 * @Date: 2023-12-19 23:04:12 
 * @Last Modified by: xiaosihan
 * @Last Modified time: 2024-03-17 20:25:31
 */

import homeStore from "@views/home/homeStore";
import { autorun, toJS } from "mobx";
import { DoubleSide, Euler, Group, InstancedBufferAttribute, InstancedMesh, Color, Matrix4, MeshStandardMaterial, MeshLambertMaterial, Quaternion, Vector3 } from "three";
import threeUtils from "three-base/threeUtils";
import { degToRad } from "three/src/math/MathUtils";
import InstancedGroupMesh from "../InstancedGroupMesh";
import MeshBase from "../MeshBase/MeshBase";
import publicMaterial from "./publicMaterial";
import { WindowData } from "@views/home/type";
import { cloneDeep, throttle, uniq } from "lodash";
import { debug } from "console";
import utils from "@utils";

// 窗户组
export default class WindowGroup extends Group {
    constructor() {
        super();
    }

    static _position: Vector3 = new Vector3();
    static _scale: Vector3 = new Vector3(1, 1, 1);
    static _rotation: Euler = new Euler();
    static _Quaternion: Quaternion = new Quaternion();
    static _Matrix4: Matrix4 = new Matrix4();

    static colorMap = new Map<string, Color>();

    //实例模型组
    group = (() => {
        const group = new class InstanceGroup extends Group {
            constructor() {
                super();
            }
            //实例模型组
            declare children: Array<InstancedGroupMesh>;
        }();

        this.add(group);
        return group;
    })();

    //根据url 获取实例模型组  url
    async getInstancedMeshGroupByUrl(url: string): Promise<InstancedGroupMesh> {

        let instancedMeshGroup = this.group.children.find(o => o.url === url);

        if (!instancedMeshGroup) {
            const meshBase = new MeshBase(url);
            await new Promise<void>((resolve, reject) => {
                meshBase.addEventListener("loaded", () => {
                    meshBase.traverseMesh(mesh => {
                        mesh.geometry.computeVertexNormals();
                        if (["平面"].includes(mesh.name)) {
                            mesh.material = publicMaterial.glassMat;
                        } else {
                            mesh.material = new MeshStandardMaterial({
                                color: "#ffffff",
                                side: DoubleSide,
                                metalness: 0.3,
                                roughness: 0,
                                flatShading: true
                            });
                        }
                    })
                    threeUtils.scaleTo(meshBase, 0.2);
                    resolve();
                });
            });

            threeUtils.center(meshBase);
            threeUtils.alignBottom(meshBase);

            instancedMeshGroup = new InstancedGroupMesh(meshBase, 0);

            instancedMeshGroup.children.map(o => {
                o.userData = {
                    cursor: "pointer",
                    enableEvent: true,
                };

                o.addEventListener("click", e => {
                    //@ts-ignore
                    console.log("e.instanceId", e.instanceId);
                });

                //窗户双击事件
                // o.addEventListener("dbclick", e => {
                //     console.log(o.name);
                //     //@ts-ignore
                //     utils.materialEditor(o.name, o.material);
                // });
            })

            instancedMeshGroup.url = url;
        }

        if (!this.group.children.some(f => f.url === url)) {
            this.group.add(instancedMeshGroup);
        }

        return instancedMeshGroup;
    }

    // 更新窗户的实例矩阵
    // 更新实例矩阵
    async updataInstancedMesh(floorNum: number, windowNum: { east: number; south: number; }, floorHeight: number, windowDatas: Array<WindowData>) {
        const { state, showState } = homeStore;
        const activeType = toJS(homeStore.activeType);
        const activeInstall = toJS(homeStore.activeInstall);

        //按照url 分组
        const urls = uniq(windowDatas.map(d => d.url));

        //先全部隐藏
        this.group.children.find(o => o.visible = false);

        // 遍历 url 计算每个窗户的实例矩阵数量
        for (let url of urls) {
            // 按照 url来获取 InstancedMeshGroup 对象
            const instancedMeshGroup = await this.getInstancedMeshGroupByUrl(url);
            instancedMeshGroup.visible = true; // 用到的就显示出来
            instancedMeshGroup.count = windowDatas.length;
            instancedMeshGroup.children.map((instancedMesh: InstancedMesh) => {
                instancedMesh.instanceMatrix = new InstancedBufferAttribute(new Float32Array(windowDatas.length * 16), 16);
                instancedMesh.instanceColor = new InstancedBufferAttribute(new Float32Array(windowDatas.length * 3), 3);
                instancedMesh.count = instancedMeshGroup.count;
            });
        }

        // 更具窗户数据更新实例矩阵
        for (let i = 0; i < windowDatas.length; i++) {
            const w = windowDatas[i];
            WindowGroup._position.copy(w.position);
            WindowGroup._Quaternion.setFromEuler(w.rotation);
            WindowGroup._Matrix4.compose(WindowGroup._position, WindowGroup._Quaternion, WindowGroup._scale);

            const instancedMeshGroup = await this.getInstancedMeshGroupByUrl(w.url);
            instancedMeshGroup.setMatrixAt(i, WindowGroup._Matrix4);

            // 根据状态设置颜色
            instancedMeshGroup.setColorAt(i, instancedMesh => {
                const c = (activeType.includes(w.name) || activeInstall.includes(w.installState)) ? (state === "distribution" ? w.color : w.installColor) : "#ffffff";
                if (!WindowGroup.colorMap.has(c)) {
                    WindowGroup.colorMap.set(c, new Color(c));
                }
                return WindowGroup.colorMap.get(c);
            });
        }

        this.group.children.find(o => {
            o.computeBoundingSphere();
        });

    };

    // 设置数据
    async setData(floorNum: number, windowNum: { east: number; south: number; }, windowDatas: Array<WindowData>) {
        const { floorHeight } = homeStore;
        this.floorNum = floorNum;
        this.windowNum = windowNum;
        this.windowDatas = windowDatas;
        await this.updataInstancedMesh(this.floorNum, this.windowNum, floorHeight, this.windowDatas);
    }


    floorNum: number = 1;
    windowNum: { east: number; south: number; } = { east: 1, south: 1 };
    windowDatas: Array<WindowData> = [];

    dispose = autorun(() => {
        const { floorHeight, state } = homeStore;
        if (state) {
            this.updataInstancedMesh(this.floorNum, this.windowNum, floorHeight, this.windowDatas);
        }
    }, { delay: 10 });

}